package com.example.util;

import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@Component
public class RedisClient {

    @Resource
    RedisTemplate<String, Object> redisTemplate;

    public boolean setExpire(String key, long timeout) {
        return redisTemplate.expire(key, timeout, TimeUnit.SECONDS);
    }

    /**
     * Set {@code value} for {@code key}.
     *
     * @param key   must not be {@literal null}.
     * @param value
     * @see <a href="http://redis.io/commands/set">Redis Documentation: SET</a>
     */
    public boolean set(String key, Object value) {
        try {
            redisTemplate.opsForValue().set(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * Set the {@code value} and expiration {@code timeout} for {@code key}.
     *
     * @param key     must not be {@literal null}.
     * @param value
     * @param timeout timeout of seconds
     * @see <a href="http://redis.io/commands/setex">Redis Documentation: SETEX</a>
     */
    public void set(String key, String value, long timeout) {
        redisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS);
    }

    public boolean expire(String key, long time) {
        try {
            if (time > 0) {
                redisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    public boolean setNX(String key) {
        boolean success = false;
        try {
            success = redisTemplate.opsForValue().setIfAbsent(key, "1");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return success;
    }

    /**
     * @param key
     * @return
     */
    public Set<Object> sGet(String key) {
        try {
            return redisTemplate.opsForSet().members(key);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * @param key
     * @return
     */
    public boolean exists(String key) {
        return redisTemplate.hasKey(key);
    }

    /**
     * @param key
     */
    public void del(String key) {
        redisTemplate.delete(key);
    }

    /**
     * Increment 1 value stored as string value under {@code key}.
     *
     * @param key must not be {@literal null}.
     * @see <a href="http://redis.io/commands/incr">Redis Documentation: INCR</a>
     */
    public long incrbyone(String key) {
        return redisTemplate.opsForValue().increment(key, 1);
    }

    public long incrby(String key, int num) {
        return redisTemplate.opsForValue().increment(key, num);
    }

    /**
     * Get the value of {@code key}.
     *
     * @param key must not be {@literal null}.
     * @see <a href="http://redis.io/commands/get">Redis Documentation: GET</a>
     */
    public Object get(String key) {
        return redisTemplate.opsForValue().get(key);
    }

    /**
     * Increment {@code value} of a hash {@code hashKey} by the given {@code delta}.
     *
     * @param key     must not be {@literal null}.
     * @param hashKey must not be {@literal null}.
     * @return return the final value
     */
    public Long hincrbyone(String key, String hashKey) {
        return redisTemplate.opsForHash().increment(key, hashKey, 1);
    }

    /**
     * Increment {@code value} of a hash {@code hashKey} by the given {@code delta}.
     *
     * @param key     must not be {@literal null}.
     * @param hashKey must not be {@literal null}.
     * @return return the final value
     */
    public Long hincrby(String key, String hashKey, int num) {
        return redisTemplate.opsForHash().increment(key, hashKey, num);
    }

    /**
     * Get key set (fields) of hash at {@code key}.
     *
     * @param key must not be {@literal null}.
     * @return all field of hash set
     */
    public void hclear(String key) {
        Set<Object> fields = redisTemplate.opsForHash().keys(key);
        for (Object field : fields) {
            redisTemplate.opsForHash().delete(key, field);
        }
    }

    /**
     * Get value for given {@code hashKey} from hash at {@code key}.
     *
     * @param key     must not be {@literal null}.
     * @param hashKey must not be {@literal null}.
     * @return return the value of field of hash
     */
    public Object hget(String key, String hashKey) {
        return redisTemplate.opsForHash().get(key, hashKey);
    }

    public boolean hMSet(String key, Map<String, ? extends Object> map, long time) {
        try {
            redisTemplate.opsForHash().putAll(key, map);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * Get all value for given hash at {@code key}.
     *
     * @param key must not be {@literal null}.
     * @return return the map of hash
     */
    public Map<String, Object> hgetall(String key) {
        Map<String, Object> allValues = new HashMap<>();
        HashOperations<String, String, Object> operations = redisTemplate.opsForHash();
        Map<String, Object> entries = operations.entries(key);
        Iterator it = entries.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry pair = (Map.Entry) it.next();
            allValues.put(pair.getKey().toString(), pair.getValue());
        }
        return allValues;
    }

    public Long hincrbyNum(String key, String hashKey, Integer delta) {
        return redisTemplate.opsForHash().increment(key, hashKey, delta);
    }

    public Map<Object, Object> hMGet(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * Set the {@code value} of a hash {@code hashKey}.
     *
     * @param key     must not be {@literal null}.
     * @param hashKey must not be {@literal null}.
     * @param value
     */
    public void hset(String key, String hashKey, String value) {
        redisTemplate.opsForHash().put(key, hashKey, value);
    }

    /**
     * sadd 将一个或多个成员元素加入到集合中，已经存在于集合的成员元素将被忽略。
     *
     * @param key
     * @param values
     * @return
     */
    public Long sadd(String key, String... values) {
        return redisTemplate.opsForSet().add(key, values);
    }

    /**
     * isMmeber 判断成员元素是否是集合的成员
     *
     * @param key
     * @param object
     * @return
     */
    public boolean isMmeber(String key, Object object) {
        Boolean member = redisTemplate.opsForSet().isMember(key, object);
        return member;
    }

    /**
     * sismember 判断成员元素是否是集合的成员
     *
     * @param key
     * @return
     */
    public long scard(String key) {
        return redisTemplate.opsForSet().size(key);
    }

    /**
     * srem 移除集合中的一个或多个成员元素，不存在的成员元素会被忽略。
     *
     * @param key
     * @param value
     * @return
     */
    public long srem(String key, String value) {
        return redisTemplate.opsForSet().remove(key, value);
    }

    /**
     * zadd 将一个或多个成员元素及其分数值加入到有序集当中。
     *
     * @param key
     * @param value
     * @param score
     * @return
     */
    public Boolean zadd(String key, String value, Double score) {
        return redisTemplate.opsForZSet().add(key, value, score);
    }

    /**
     * ZINCRBY  对有序集合中指定成员的分数加上增量 increment
     *
     * @param key
     * @param value
     * @param score
     * @return
     */
    public Double zincrby(String key, String value, Double score) {
        return redisTemplate.opsForZSet().incrementScore(key, value, score);
    }

    /**
     * zrevrank 返回有序集中成员的排名。其中有序集成员按分数值递减(从大到小)排序。
     *
     * @param key
     * @param start
     * @param stop
     * @return
     */
    public Set<Object> zrevrang(String key, Long start, Long stop) {
        return redisTemplate.opsForZSet().reverseRange(key, start, stop);
    }


    public Long rank(String key, Object value) {
        Long rank = redisTemplate.opsForZSet().reverseRank(key, value);
        return rank + 1;
    }


    /**
     * Zrem 移除有序集中的一个或多个成员，不存在的成员将被忽略。
     *
     * @param key
     * @param start
     * @param stop
     * @return
     */
    public Long zremrange(String key, Long start, Long stop) {
        return redisTemplate.opsForZSet().removeRange(key, start, stop);
    }

    /**
     * Zscore 命令返回有序集中，成员的分数值。 如果成员元素不是有序集 key 的成员，或 key 不存在，返回 nil
     *
     * @param key
     * @param member
     * @return
     */
    public Double zscore(String key, String member) {
        return redisTemplate.opsForZSet().score(key, member);
    }

}
